package net.guruqu.rvoView;

import java.awt.Canvas;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.XMLDecoder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;

import com.gibbon.jme.context.JmeContext;
import com.gibbon.jme.context.RenderPass;
import com.jme.bounding.BoundingVolume;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Node;
import com.jme.scene.SharedNode;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.Spatial.TextureCombineMode;
import com.jme.scene.state.LightState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;

public class SimulationView {
	SimulationRenderPass myRenderPass;
	Canvas canvasInstance;
	ModelRepository modelRepository;
	SimulationAdapter simulationAdapter;
	HashMap<String, String> properties;
	boolean simulationPaused;
	boolean initApplication;
	float simulationSpeed;
	RvoSetupInfo setupInfo;

	public static final int CM_WHOLEVIEW = 0;
	public static final int CM_CHASE_CHR = 1;
	// private int chaseAgent;
	// private int cameraMode;
	public static final String MD_ROADMAP_FILE = "MD_ROADMAP_FILE";
	CameraControl cameraControl;

	public CameraControl getCameraControl() {
		return cameraControl;
	}

	public void setCameraControl(CameraControl cameraControl) {
		this.cameraControl = cameraControl;
	}

	public SimulationView(HashMap<String, String> prop,
			ModelRepository modelRepository) {
		this.properties = prop;
		this.modelRepository = modelRepository;
		simulationAdapter = new SimulationAdapter();
		simulationPaused = true;
		simulationSpeed = 0.25f;
	}

	public void restartSimulation() {
		simulationAdapter.restart();
	}

	public void loadMapFile() {
		try {
			String saveFile = properties.get(MD_ROADMAP_FILE);
			XMLDecoder decoder;
			decoder = new XMLDecoder(new FileInputStream(saveFile));
			setupScenario((RvoSetupInfo) decoder.readObject());
			canvasInstance.repaint();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	class SimulationRenderPass extends RenderPass {
		KeyInputAdapter keyAdapter;
		MouseInputAdapter mouseAdapter;
		Node root;
		float aspectRatio;
		boolean asChanged;

		// Vector3f cameraLocation;
		// float cameraAngle;
		// float cameraDistance;
		// float cameraLat;
		// Vector3f rotateCenter;

		// public Vector3f getCameraLocation() {
		// return cameraLocation;
		// }
		//
		// public void setCameraLocation(Vector3f cameraLocation) {
		// this.cameraLocation.set(cameraLocation);
		// updateCamera();
		// }

		// public void setCameraCenter(Vector3f cameraLocation) {
		// this.rotateCenter.set(cameraLocation);
		// updateCamera();
		// }

		public SimulationRenderPass() {
			aspectRatio = 640f / 480f;
			mouseAdapter = new MouseInputAdapter(new InputAction() {
				public void performAction(InputActionEvent evt) {
				}
			});
			keyAdapter = new KeyInputAdapter(new InputAction() {
				public void performAction(InputActionEvent evt) {
				}
			});
			// cameraLocation = new Vector3f(100, 100, 100);
			// rotateCenter = new Vector3f();
		}

		private void processSimulationEvent() {
			if (initApplication == true) {
				simulationAdapter.initScenario(setupInfo);
				initApplication = false;
			}
		}

		//
		// private void updateChaseCamera() {
		// SceneAgent agent = simulationAdapter.getSceneAgents().get(
		// chaseAgent);
		// BoundingVolume bv = agent.getWorldBound();
		// float height = 10000 - bv.distanceToEdge(new Vector3f(0, 10000, 0));
		// Vector3f v3f = new Vector3f(agent.getLocalTranslation());
		// // v3f.y += height;
		// setCameraCenter(v3f);
		// }

		@Override
		protected void doUpdate(JmeContext context) {
			// TODO Auto-generated method stub
			try {
				mouseAdapter.update();
				keyAdapter.update();
				if (asChanged) {
					updateCameraConfig();
				}
				if (!simulationPaused) {
					simulationAdapter.update(simulationSpeed);
				}
				if (cameraControl != null)
					cameraControl.updateCamera(0, context.getRenderer()
							.getCamera());
				// switch (cameraMode) {
				// case CM_CHASE_CHR:
				// updateChaseCamera();
				// break;
				// case CM_WHOLEVIEW:
				// setCameraCenter(new Vector3f());
				// break;
				// }
				// if (cameraNeedUpdate)
				// updateCameraLocal();
				processSimulationEvent();

			} catch (Exception e) {
				e.printStackTrace();
			}
			root.updateModelBound();
			root.updateWorldBound();
		}

		private void updateCameraConfig() {
			Camera camera = JmeContext.get().getRenderer().getCamera();
			camera.setParallelProjection(false);
			camera.setFrustumPerspective(45, aspectRatio, 1, 1e5f);
			camera.update();
			asChanged = false;
			canvasInstance.repaint();
		}

		@Override
		protected void doRender(JmeContext context) {
			// TODO Auto-generated method stub
			DisplaySystem display = DisplaySystem.getDisplaySystem();
			Renderer renderer = display.getRenderer();
			renderer.clearColorBuffer();
			renderer.clearZBuffer();
			renderer.draw(root);

			if (renderer.isInOrthoMode()) {
			} else {
				renderer.setOrtho();
				renderer.unsetOrtho();
			}
			if (!simulationPaused)
				canvasInstance.repaint();
		}

		public float getAspectRatio() {
			return aspectRatio;
		}

		public KeyInputAdapter getKeyAdapter() {
			return keyAdapter;
		}

		public MouseInputAdapter getMouseAdapter() {
			return mouseAdapter;
		}

		public void setAspectRatio(float aspectRatio) {
			this.aspectRatio = aspectRatio;
			asChanged = true;
			// updateCamera();
		}

		// boolean cameraNeedUpdate;
		//
		// public void updateCamera() {
		// cameraNeedUpdate = true;
		// }

		// public Vector3f calculateCameraLocation() {
		//
		// float y = cameraDistance * FastMath.sin(cameraLat) + rotateCenter.y;
		// float theta = cameraDistance * FastMath.cos(cameraLat);
		// float x = FastMath.cos(cameraAngle) * theta + rotateCenter.x;
		// float z = FastMath.sin(cameraAngle) * theta + rotateCenter.z;
		// // System.out.println("X:" + x + " Y:" + y + " Z:" + z + " LAT:"
		// // + cameraLat);
		// return new Vector3f(x, y, z);
		// }

		public void resizeCanvas(int w, int h) {
			setAspectRatio((float) w / (float) h);
		}

		@Override
		public void initPass(JmeContext context) {
			modelRepository.stow();
			// cameraAngle = FastMath.PI / 4;
			// cameraDistance = 300;
			// cameraLat = FastMath.PI / 4;

			Camera camera = context.getRenderer().createCamera(640, 480);
			camera.setParallelProjection(false);
			// camera.setFrustumPerspective(45, 640.0f / 480.0f, 1, 1000);
			// setCameraLocation(calculateCameraLocation());
			// camera.setLocation(cameraLocation);
			camera.setFrustumPerspective(45, aspectRatio, 1, 1e5f);
			// camera.lookAt(new Vector3f(0, 0, 0), new Vector3f(0, 1, 0));
			camera.update();

			context.getRenderer().setCamera(camera);
			context.getRenderer().setBackgroundColor(ColorRGBA.gray);

			root = new Node("root");
			{
				Node node = null;
				node = (Node) modelRepository.getRepository().get("env");
				SharedNode sn = new SharedNode(node);
				root.attachChild(sn);
			}
			// {
			// Node nn = new Node();
			// nn.attachChild((Spatial) (ModelRepository.get().getRepository()
			// .get("agent")));
			// SharedNode node2 = new SharedNode(nn);
			// root.attachChild(node2);
			// }

			Node agentNode = simulationAdapter.getRenderNode();
			root.attachChild(agentNode);

			LightState lightState = context.getRenderer().createLightState();
			DirectionalLight directionalLight = new DirectionalLight();
			directionalLight.setDiffuse(new ColorRGBA(0.7f, 0.7f, 0.7f, 1));
			directionalLight.setDirection(new Vector3f(-0.577f, -0.577f,
					-0.577f));
			directionalLight.setAmbient(new ColorRGBA(0.3f, 0.3f, 0.3f, 1));
			directionalLight.setEnabled(true);
			directionalLight.setShadowCaster(true);
			// lightState.setGlobalAmbient(new ColorRGBA(0.0f, 0.0f, 0.0f, 1));
			lightState.attach(directionalLight);
			lightState.setEnabled(true);
			root.setRenderState(lightState);

			ZBufferState bufferState = context.getRenderer()
					.createZBufferState();
			bufferState.setEnabled(true);
			bufferState.setFunction(ZBufferState.TestFunction.LessThan);
			root.setRenderState(bufferState);
			// root.setTextureCombineMode(TextureCombineMode.Off);
			// root.setLightCombineMode(LightCombineMode.Off);
			root.updateRenderState();
			root.updateModelBound();
			canvasInstance.repaint();
		}
	}

	public void setupScenario(RvoSetupInfo info) {
		initApplication = true;
		this.setupInfo = info;
	}

	// public void setCameraLat(float lat) {
	// if (myRenderPass != null) {
	// myRenderPass.cameraLat = lat;
	// myRenderPass.updateCamera();
	// }
	// }
	//
	// public void setCameraDistance(float dist) {
	// if (myRenderPass != null) {
	// myRenderPass.cameraDistance = dist;
	// myRenderPass.updateCamera();
	// }
	// }
	//
	// public void setCameraAngle(float ang) {
	// if (myRenderPass != null) {
	// myRenderPass.cameraAngle = ang;
	// myRenderPass.updateCamera();
	// }
	// }

	// public float getCameraLat() {
	// if (myRenderPass != null) {
	// return myRenderPass.cameraLat;
	// }
	// return 0;
	// }
	//
	// public float getCameraDistance() {
	// if (myRenderPass != null) {
	// return myRenderPass.cameraDistance;
	// }
	// return 0;
	// }
	//
	// public float getCameraAngle() {
	// if (myRenderPass != null) {
	// return myRenderPass.cameraAngle;
	// }
	// return 0;
	// }
	
//	public void switchCanvas(JmeContext context){
//		
//	}

	public void setCanvas(JmeContext context) {
//		if (canvasInstance != null)
//			throw new RuntimeException("Canvas Instance already set!");
		Canvas canvas = context.getCanvas();
		canvasInstance = canvas;
		if (myRenderPass == null)
			myRenderPass = new SimulationRenderPass();
		context.getPassManager().add(myRenderPass);
		
//		ShadowedRenderPass pass = new ShadowedRenderPass();
//		pass.add(myRenderPass.root);
//		pass.setRenderShadows(true);
//		pass.setLightingMethod(ShadowedRenderPass.LightingMethod.Additive);
//		context.getPassManager().add(pass);
		
		canvasInstance.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent ce) {
				myRenderPass.resizeCanvas(canvasInstance.getWidth(),
						canvasInstance.getHeight());
			}
		});
		myRenderPass.getMouseAdapter().setup(canvasInstance);
		canvasInstance.repaint();
	}

	public static void main(String s[]) {

	}

	public boolean isSimulationPaused() {
		return simulationPaused;
	}

	public void setSimulationPaused(boolean simulationPaused) {
		this.simulationPaused = simulationPaused;
		if (!simulationPaused)
			canvasInstance.repaint();
	}

	public float getSimulationSpeed() {
		return simulationSpeed;
	}

	public void setSimulationSpeed(float simulationSpeed) {
		this.simulationSpeed = simulationSpeed;
	}

	public SimulationAdapter getSimulationAdapter() {
		return simulationAdapter;
	}

	// public int getChaseAgent() {
	// return chaseAgent;
	// }
	//
	// public void setChaseAgent(int chaseAgent) {
	// this.chaseAgent = chaseAgent;
	// }

	// public int getCameraMode() {
	// return cameraMode;
	// }

	// public void setCameraMode(int cameraMode) {
	// this.cameraMode = cameraMode;
	// canvasInstance.repaint();
	// }
}
